home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / gnuc25.zip / C.E < prev   
Text File  |  1992-07-29  |  48KB  |  1,795 lines

  1. // c.e 
  2. // 
  3. // C code editing commands for Epsilon 6.00 
  4. // 
  5. // Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.  
  6. // Copyright (C) 1991, K. Shane Hartman and the Free Software 
  7. // Foundation, Inc.  
  8. // 
  9. // This file was part of GNU Emacs but only superficially resembles 
  10. // the original Emacs LISP code.  
  11. // 
  12. // This file is free software; you can redistribute it and/or modify 
  13. // it under the terms of the GNU General Public License as published 
  14. // by the Free Software Foundation; either version 1, or (at your 
  15. // option) any later version.  
  16. // 
  17. // This file is distributed in the hope that it will be useful, but 
  18. // WITHOUT ANY WARRANTY; without even the implied warranty of 
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  20. // General Public License for more details (the file COPYING) 
  21. // 
  22. // If you want a copy of the GNU General Public License, write to the 
  23. // Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  24. // 
  25. // 
  26. // Most of this code was ported to EEL by me (KSH) from Gnu Emacs 
  27. // lisp sources.  The remainder was created by me to facilitate the 
  28. // port.  All code is Copyleft by K. Shane Hartman and the Free 
  29. // Software Foundation.  See the file COPYING for specific terms of 
  30. // the license.  
  31. // 
  32. // This file is a straight replacement for Lugaru's C.E.  Just 
  33. // compile load and save state.  You may want to play with the 
  34. // control variables to get the style you want.  See below.  
  35. // 
  36. // WARNING: The Gnu C indenter is very good, but searches a lot.  If
  37. // your machine is wimpy, I would stick with Lugaru's indenter which
  38. // is faster and dumber.  I use a 486/33 so I don't notice the extra
  39. // computation.  Also, I use a limited lookback parser to handle most
  40. // of the simple cases which assumes the indentation style used in
  41. // this file.  The general indenter is used for hairy cases.  It is
  42. // disabled by default, but see variable c-indenter-shortcut.
  43. // Without the quick-indenter, large functions require noticeable but
  44. // tolerable time to indent.  The reason is that Lugaru's regex
  45. // searches really suck.  (07/10/92 JBK Ripped out the lookback
  46. // parser.  The performance benefits have become less obvious as the
  47. // general indenter has been tuned over time).  
  48. // 
  49. // Note the commands (refer to C.DOC or the code for details): 
  50. // 
  51. //        forward_cexp() 
  52. //        backward_cexp() 
  53. //        kill_cexp() 
  54. //        start_of_defun() 
  55. //        end_of_defun() 
  56. //        up_level() 
  57. //        indent_function() 
  58. //        mark_function() 
  59. //        list_functions() 
  60. // 
  61. // Revision History: 
  62. // 
  63. //    Send bug reports, bug fixes to shane@ai.mit.edu 
  64. // 
  65. //        Version 1.0: 06/01/91 shane@ai.mit.edu Initial version.  
  66. // 
  67. //        Version 1.1: 06/20/91 shane@ai.mit.edu Some bug fixes.  
  68. // 
  69. //        Version 1.2: 06/28/91 shane@ai.mit.edu Improve performance of
  70. //        looking_at slightly.  
  71. // 
  72. //        Version 1.3: 07/08/91 shane@ai.mit.edu Removed is_word_char,
  73. //        use Lugaru's.  Also added variable c_tab_always_indent.  
  74. // 
  75. //        Version 1.4: 07/12/91 shane@ai.mit.edu Made C-M-A a little
  76. //        smarter.  
  77. // 
  78. //        Version 1.5: 07/19/91 shane@ai.mit.edu Fix problem with
  79. //        indenting lines after disgusting c++ comments.  
  80. // 
  81. //        Version 1.6: 07/18/91 More fun with c++ comments.  Fix
  82. //        c_backward_to_noncomment to understand them.  
  83. // 
  84. //        Version 1.7: 07/18/91 More fun with c++ comments.  Fix bug in
  85. //        skip_c_comments to understand them. Make
  86. //        c_backward_to_start_of_if stop if it detects a syntax error.
  87. //        Stop binding case_fold many times.  Do it once in outermost
  88. //        call and use unwind-protect (C's miserable excuse for it,
  89. //        that is).  
  90. // 
  91. //        Version 1.8: 08/05/91 shane@ai.mit.edu Improve performance of
  92. //        CR between functions if c_indenter_shortcut is T.  Flush
  93. //        c_backward_to_start_of_do, I don't think it does anything.
  94. //        Do case folding in C-M-Q (indent-function).  Halt infinite
  95. //        loop for #thing ... \ at file begin.  Make the effect of
  96. //        c_brace_offset and c_case_offset more predictable for
  97. //        settings I don't use.  Remove unused variable
  98. //        c_continued_brace_offset.  Use c_brace_offset for this.  
  99. // 
  100. //        Version 1.9: 08/12/91 johnk@wrq.com (John Kercheval) Fix the
  101. //        indent-function command to use spots to remember the region
  102. //        it is indenting since the region changes while indenting.
  103. //        Use beginning_of_defun to determine the true function start
  104. //        rather than the macro BEGINNING_OF_DEFUN(). Broke out
  105. //        beginning_of_defun command guts and renamed command
  106. //        beginning-of-defun to start-of-defun to support this (the
  107. //        command recenters the window).  Fix command do-c-indent to
  108. //        handle tab correctly when !c_tab_always_indent and epsilon is
  109. //        doing space to tab translation.  Fix c_indenter_1 to handle
  110. //        statement after case correctly (use current_indentation not
  111. //        current_column).  
  112. // 
  113. //        08/12/91 shane@ai.mit.edu Fix indenter to work correctly when
  114. //        there are no characters after the insertion point.  
  115. // 
  116. //        Version 1.10: 08/13/91 shane@ai.mit.edu Restore
  117. //        c_backward_to_start_of_do.  It handles do - while statements
  118. //        after ifs, etc.  
  119. // 
  120. //        Version 1.11: 09/03/91 shane@ai.mit.edu CC is C++ file
  121. //        extension.  CPP is is C++ file extension.  Fix bug with foo;
  122. //        // comment in c_backward_to_noncomment.  
  123. // 
  124. //        Version 1.12: 11/13/91 shane@ai.mit.edu Had to bind
  125. //        do_c_newline to CTRL('M') in Epsilon 5.0.3, spec '\n' no
  126. //        longer seems to invoke do-c-newline on enter.  Added
  127. //        mark-defun (I would put it on C-M-H but Epsilon treats this
  128. //        the same as ALT-Backspace which sucks (they must have made
  129. //        key handling compatible with ix).  Made
  130. //        calculate-simple-c-indent default to hairy indenter when it
  131. //        detects "foo ( bar \n && ...".  Ignore c-brace-offset if
  132. //        brace is toplevel.  
  133. // 
  134. //        Version 1.13: 11/14/91 shane@ai.mit.edu Made '{' and '}'
  135. //        commands respect auto_indent variable. Changed mode name to
  136. //        "NewC" rather than "GnuC".  Added command list-defuns on A-#.
  137. //        Fixed C++ comment bug reported by Caleb Epstein.  Fixed C++
  138. //        public: bug noted by mariogo@microsoft.com.  
  139. // 
  140. //        Version 2.00: 07/10/92 johnk@wrq.com (John Kercheval) Remove
  141. //        lots of bits and pieces of code hanging around.  Use save_var
  142. //        and save_spot for the protect unwind code and spot
  143. //        allocations.  Speed up and reduce size of looking_at().  Port
  144. //        to V6.0 Epsilon sources.  Removed the quick indent code in
  145. //        favor of the full GNU indenter.  Renamed list_defuns to
  146. //        list_functions and modified to work with old style c function
  147. //        declarations.  
  148. // 
  149. //        Version 2.01: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
  150. //        in skip_chars() when at the end of a buffer and called in
  151. //        reverse direction.  Fix a bug in parse_partial_cexp() dealing
  152. //        with C++ style comments (the from and to parameters were
  153. //        being ignored during the incomment state boolean release).
  154. //        Fixed fundamental bug in do_backward_to_noncomment() which
  155. //        prevented correct reverse searches over C++ style comments
  156. //        (this included addition of a new state variable to mark the
  157. //        beginning of the last comment).  
  158. // 
  159. //        Version 2.02: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
  160. //        in parse_partial_cexp() dealing with C++ style comments (one
  161. //        of the edge cases required >= rather than >).  
  162. // 
  163. //        Version 2.03: 07/21/92 johnk@wrq.com (John Kercheval) Add
  164. //        several syntax elements which will result in an autoindent on
  165. //        newline (was case, labels and a particular else and while
  166. //        syntax).  Those added were: general while, else, for, if and
  167. //        switch syntaxes.  Remove unneeded do loop and state query in
  168. //        calculate_c_indent().  Fix bug in calculate_c_indent() which
  169. //        would result in incorrect indent on first case within a
  170. //        switch and incorrectly backed up over continued parameter
  171. //        list.  Comment indenter module in some detail.  Fixed a bug
  172. //        in parse_partial_cexp(), the terminating condition on the
  173. //        while loop did not take into account multi-character search
  174. //        criteria, the point to end location comparison was changed to
  175. //        a matchstart to end location comparison.  Moved to C++ style
  176. //        line comments to aid in clarity and comment fills.  Added the
  177. //        boolean variable c_line_comment_align to facilitate in-line
  178. //        C++ style comment blocks outside of the current indent level
  179. //        similar to the built in behavior for standard C style
  180. //        comments.
  181. //
  182. //        Version 2.04: 07/22/92 johnk@wrq.com (John Kercheval) Fix a
  183. //        newly introduced bug within calculate_c_indent().  The new
  184. //        variable c_line_comment_align was affecting lines with
  185. //        trailing comments as well as full line comments.
  186. //
  187. //        Version 2.05: 07/29/92 johnk@wrq.com (John Kercheval) Fix a
  188. //        bug in c_indenter_1() for the case where indenting an else
  189. //        cuddling a brace.  We were backing up one cexp to the opening
  190. //        brace when we should have been backing up one more step to
  191. //        the opening paren on the if block.  Do some more commenting
  192. //        and add the EDOC extension (C.DOC).
  193. //
  194.  
  195.  
  196. #include "eel.h"
  197.  
  198. typedef struct pstate {
  199.     int incomment;
  200.     int beginning_of_comment;
  201.     int instring;
  202.     int level;
  203.     int containing_cexp;
  204.     int quoted;
  205.     int beginning_of_defun;
  206. }   PSTATE;
  207.  
  208.  
  209. ////////////////////////////////////////////////////////////////////////////
  210. //
  211. // The following variables control indentation style.
  212. //
  213.  
  214. //
  215. // Indentation of C statements with respect to containing block.
  216. // This is in spaces but is filled with tabs if tab-size is smaller
  217. // than this value or the computed indent.  This is the *standard*
  218. // indentation level.
  219. //
  220. int c_indent_level = 4;
  221.  
  222. //
  223. // Imagined indentation of a C open brace that actually follows a
  224. // statement.  This will result in an indentation for braced blocks
  225. // larger than that for continuation lines.  This value affects only
  226. // code within the brace, not the brace indent itself.
  227. //
  228. int c_brace_imaginary_offset = 0;
  229.  
  230. //
  231. // Extra indentation for braces, compared with other text in same
  232. // context.  This value increases the depth of the brace.  A brace
  233. // offset of 0 will line a brace on the following line at the same
  234. // indent level as the previous statement.  This value specifies the
  235. // number of additional spaces to add to this indent.
  236. //
  237. int c_brace_offset = 0;
  238.  
  239. //
  240. // Indentation level of declarations of C function arguments.  This
  241. // specifies the indent level of non-ansi declarations in spaces.
  242. //
  243. int c_argdecl_indent = 4;
  244.  
  245. //
  246. // Offset of C label lines (including C++ class keywords) relative to
  247. // usual indentation level.  This level is normally a negative number.
  248. //
  249. int c_label_offset = -2;
  250.  
  251. //
  252. // Offset of C case statements relative to usual indentation.  The
  253. // case statement will add this indent offset to the case blocks
  254. // within the switch statement.  The *usual* indentation is
  255. // c_indent_level as set above.
  256. //
  257. int c_case_offset = 0;
  258.  
  259. //
  260. // Extra indent for lines not starting new statements such as
  261. // non-braced statements after while, if, for, etc.  This does not
  262. // effect function parameters, paren expression, etc. (which are
  263. // lined up with the open paren).
  264. //
  265. int c_continued_statement_offset = 4;
  266.  
  267. //
  268. // This BOOL value affects the alignment of the indent following a
  269. // line comment (C++).  If this value is TRUE then the line following
  270. // a C++ line comment will be indented to be even with the previous
  271. // line if the previous line is also a non-trailing line comment
  272. // (blank lines *are* significant).  This facilitates block and fill
  273. // comments which are not at normal indentation levels.  If this
  274. // value is FALSE or if there is no non-trailing line comment on the
  275. // previous line then indent will procede normally.  This value will
  276. // only affect behavior when a non-trailing comment is on the
  277. // previous line *and* is at a different indentation level than the
  278. // current code block.  This is the non-modifiable default behavior
  279. // for C style comments. 
  280. //
  281. int c_line_comment_align = 1;
  282.  
  283. //
  284. // This BOOL value effects the global behavior of the indenter. A
  285. // non-zero value means TAB in C mode should always reindent the
  286. // current regardless of where in the line point is when the TAB
  287. // command is used.
  288. //
  289. int c_tab_always_indent = 0;
  290.  
  291.  
  292. ////////////////////////////////////////////////////////////////////////////
  293. //
  294. // Code Macros
  295. //
  296.  
  297. #define LOOKING_AT(pat) looking_at (1, pat)
  298.  
  299. #define INDENT_TO(indent) to_column(indent)
  300.  
  301. #define SKIP_CHARS_BACKWARD(set) skip_chars (-1, (set))
  302. #define SKIP_CHARS_FORWARD(set) skip_chars (1, (set))
  303.  
  304. #define FORWARD_SEXP() traverse_cexp (1)
  305. #define BACKWARD_SEXP() traverse_cexp (-1)
  306.  
  307. #define SEARCH_FORWARD(str) search (1, (str))
  308. #define SEARCH_BACKWARD(str) search (-1, (str))
  309.  
  310. #define BOLP() (current_column () == 0)
  311.  
  312. #define BEGINNING_OF_DEFUN() \
  313.    (re_search (-1, "^[{A-Za-z0-9$_]+[^A-Za-z0-9$_:]") ? \
  314.     ((to_begin_line()), 1) : \
  315.     ((point = 0), 0))
  316.  
  317.  
  318. ////////////////////////////////////////////////////////////////////////////
  319. //
  320. // skip_chars will move over all characters in set in the current
  321. // buffer in a particular direction
  322. //
  323.  
  324. skip_chars(dir, set)
  325.     int dir;
  326.     char *set;
  327. {
  328.     int last = size();
  329.     int offset = dir < 0 ? -1 : 0;
  330.  
  331.     while (((point < last) || (dir == -1))
  332.            && point > 0
  333.            && index(set, character(point + offset)))
  334.         point += dir;
  335. }
  336.  
  337.  
  338. ////////////////////////////////////////////////////////////////////////////
  339. //
  340. // current_indentation will return the value of the indentation of
  341. // the current line in the buffer.
  342. //
  343.  
  344. current_indentation()
  345. {
  346.     int indent;
  347.  
  348.     save_var point;
  349.  
  350.     to_indentation();
  351.     indent = current_column();
  352.     return (indent);
  353. }
  354.  
  355.  
  356. ////////////////////////////////////////////////////////////////////////////
  357. //
  358. // looking_at will return TRUE if the pattern in the given direction
  359. // is true beginning at the current point.
  360. //
  361.  
  362. looking_at(dir, pat)
  363.     int dir;
  364.     char *pat;
  365. {
  366.     save_spot point;
  367.  
  368.     if (parse_string(dir, pat, NULL))
  369.         return 1;
  370.     else
  371.         return 0;
  372. }
  373.  
  374.  
  375. ////////////////////////////////////////////////////////////////////////////
  376. //
  377. // matching_end and associated macros will return the location of the
  378. // closest matching character in the forward direction.
  379. //
  380.  
  381. char matching_ends[] = "][]}{})()\"\"''";
  382. char opening_ends[] = "[{(";
  383. char closing_ends[] = "]})";
  384.  
  385. char
  386. matching_end(c)
  387.     char c;
  388. {
  389.     char *s = index(matching_ends, c);
  390.  
  391.     if (s == NULL)
  392.         return (0);
  393.     else
  394.         return (s[1]);
  395. }
  396.  
  397. #define opening_end(c) (index (opening_ends, (c)) != NULL)
  398.  
  399. #define closing_end(c) (index (closing_ends, (c)) != NULL)
  400.  
  401.  
  402. ////////////////////////////////////////////////////////////////////////////
  403. //
  404. // is_slashified will return TRUE if the current character is a C
  405. // literal escaped by the '\'
  406. //
  407.  
  408. is_slashified(dir)
  409.     int dir;
  410. {
  411.     int pos = point;
  412.     int slash_count = 0;
  413.  
  414.     if (dir > 0)
  415.         pos--;
  416.     while (pos-- > 0 && character(pos) == '\\')
  417.         slash_count++;
  418.     return (slash_count & 1);
  419. }
  420.  
  421.  
  422. ////////////////////////////////////////////////////////////////////////////
  423. //
  424. // beginning_of_defun will move to the beginning of the current
  425. // function.  
  426. //
  427.  
  428. beginning_of_defun() {
  429.     int last = point;
  430.  
  431.     while (BEGINNING_OF_DEFUN()) {
  432.         int next = point;
  433.         int save_excursion;
  434.  
  435.         if (character(point) == '{') {
  436.             while (point > 0) {
  437.                 nl_reverse();
  438.                 save_excursion = point;
  439.                 to_begin_line();
  440.                 next = point;
  441.                 SKIP_CHARS_FORWARD(" \t");
  442.                 if (point == save_excursion) {
  443.                     nl_forward();
  444.                     break;
  445.                 }
  446.                 point = next;
  447.                 if (LOOKING_AT("#|(*.%*/)|(//)")) {
  448.                     nl_forward();
  449.                     break;
  450.                 }
  451.             }
  452.             break;
  453.         }
  454.         skip_c_comments(-1);
  455.         save_excursion = point;
  456.         to_begin_line();
  457.         if (character(point) == '#') {
  458.             point = next;
  459.             break;
  460.         }
  461.         to_end_line();
  462.         if (character(point - 1) == '\\') {
  463.             point = next;
  464.             break;
  465.         }
  466.         last = next;
  467.         point = save_excursion;
  468.         if ((character(point) == '}')
  469.             || ((character(point) == ';')
  470.                 && (point > 0)
  471.                 && ((character(point - 1) == '}')
  472.                     || (character(point - 1) == ')')
  473.                     || (BACKWARD_SEXP(),
  474.                         skip_c_comments(-1),
  475.                         character(point) == '=')))) {
  476.             point = last;
  477.             break;
  478.         }
  479.         if (point == 0)
  480.             break;
  481.         to_end_line();
  482.         point++;
  483.     }
  484. }
  485.  
  486.  
  487. ////////////////////////////////////////////////////////////////////////////
  488. //
  489. // skip_c_comments will move over all whitespace and C/C++ comments
  490. // in the given direction starting at point in current buffer.
  491. //
  492.  
  493. skip_c_comments(dir)
  494.     int dir;
  495. {
  496.     int offset = 1;
  497.     int last = size();
  498.  
  499.     if (dir != 1) {
  500.         dir = -1;
  501.         offset = 0;
  502.     }
  503.     while (re_search(dir, "[^ \t\n\f]")
  504.            && point > 0
  505.            && point < last) {
  506.         char c = character(point - offset);
  507.         char cc = character(point - offset + dir);
  508.  
  509.         if (c == '/') {
  510.             if (cc == '*')
  511.                 search(dir, dir > 0 ? "*/" : "/*");
  512.             else if (cc == '/') {    /* c++ comment */
  513.                 if (dir > 0)
  514.                     nl_forward();
  515.                 else
  516.                     point--;
  517.             }
  518.         } else {
  519.             if (dir > 0)
  520.                 point -= dir;
  521.             break;
  522.         }
  523.     }
  524. }
  525.  
  526.  
  527. ////////////////////////////////////////////////////////////////////////////
  528. //
  529. // traverse_cexp will move in the given direction to the end of the
  530. // current subexpression delineated by some C/C++ delimeter.
  531. //
  532. // KSH - This should use parse_partial_cexp but I did not port some
  533. // of the functionality it should have to do this.  This is probably
  534. // faster and I have no more time to devote to this project, so I
  535. // have left it.
  536. //
  537. // JBK - This should indeed use some sort of partial expression
  538. // parser.  The current implementation does not handle lone single or
  539. // double quotes or closing C comment characters within C++ style
  540. // line comments when traversing backwards.  If such a character
  541. // pattern is found within the line comment then several things may
  542. // result but all will result in incorrect indentation.  Speed issues
  543. // and a lack of interest (I just do not generally use these
  544. // character patterns in comments) prevent me from making the
  545. // required modifications.  Any volunteers?  Wed, 07/29/1992 23:08:40
  546. //
  547.  
  548. traverse_cexp(dir)
  549.     int dir;
  550. {
  551.     int level = 0;
  552.     int orig = point;
  553.     char c = 0;
  554.     char start = 0;
  555.     char end = 0;
  556.     int offset = 1;
  557.     char buf[2];
  558.     char patbuf[32];
  559.  
  560.     if (dir != 1) {
  561.         dir = -1;
  562.         offset = 0;
  563.     }
  564.     if (dir > 0
  565.         && point < size()
  566.         && index(":;?,", character(point)))
  567.         point++;
  568.     skip_c_comments(dir);
  569.  
  570.     //
  571.     // if we are not already on one of the open or close end
  572.     // delimiters move to the nearest one in the particular
  573.     // direction.
  574.     //
  575.     if (!index(matching_ends, character(point))) {
  576.         if (re_search(dir, "[][)(}{\"';:,? \t\n\f]")) {
  577.             point -= dir;
  578.             return (1);
  579.         }
  580.         return (0);
  581.     } else if ((opening_end(character(point)) && dir > 0)
  582.                || (closing_end(character(point)) && dir < 0)
  583.                || character(point) == '"'
  584.                || character(point) == '\'') {
  585.         //
  586.         // Move through code and comments until we get to the next
  587.         // c subexpression.
  588.         //
  589.         if (dir < 0)
  590.             point++;
  591.         strcpy(patbuf, "[][)(}{\"']|/%*|%*/|//");
  592.         while (re_search(dir, patbuf)) {
  593.             
  594.             // here should be parse_partial_cexp() calls
  595.             
  596.             buf[0] = c = character(point - offset);
  597.             buf[1] = 0;
  598.             if (start == 0) {
  599.                 start = c;
  600.                 end = matching_end(start);
  601.                 if (!end)
  602.                     start = 0;
  603.                 else {
  604.                     strcpy(patbuf, "%X|%X|[\"']|/%*|%*/|//");
  605.                     patbuf[1] = start;
  606.                     patbuf[4] = end;
  607.                 }
  608.             }
  609.             if (c == '"' || c == '\'') {
  610.                 char strpat[8];
  611.                 strcpy(strpat, "%X|\n");
  612.                 strpat[1] = c;
  613.                 while (re_search(dir, buf) && is_slashified(dir));
  614.                 if (character(point - 1) == '\n')
  615.                     break;
  616.             } else if (c == '*') {
  617.                 search(dir, dir > 0 ? "*/" : "/*");
  618.             } else if (c == '/') {    /* c++ comment */
  619.                 if (dir > 0)
  620.                     nl_forward();
  621.             }
  622.             if (c == start)
  623.                 level++;
  624.             if (c == end && !--level)
  625.                 break;
  626.         }
  627.         return (level == 0 && start != 0);
  628.     } else {
  629.         point = orig;
  630.         return (0);
  631.     }
  632. }
  633.  
  634.  
  635. ////////////////////////////////////////////////////////////////////////////
  636. //
  637. // parse_partial_cexp will move through the region specified by from
  638. // and to and determine the current indent (or block) level and state
  639. // of that region.
  640. //
  641.  
  642. parse_partial_cexp(from, to, state)
  643.     int from;
  644.     int to;
  645.     PSTATE *state;
  646. {
  647.     char c = 0;
  648.     int stack[64];
  649.  
  650.     state->instring = 0;
  651.     state->incomment = 0;
  652.     state->beginning_of_comment = 0;
  653.     state->containing_cexp = -1;
  654.     state->quoted = 0;
  655.     state->level = -1;
  656.     point = from;
  657.  
  658.     //
  659.     // parse through open and close delimiters until we get to the
  660.     // end of the region.  Track comments as well as current level
  661.     // and level start location.
  662.     //
  663.     while (re_search(1, "[][(){}\"']|/%*|//") && (matchstart < to)) {
  664.         c = character(point - 1);
  665.         if (opening_end(c)) {
  666.             //
  667.             // just store the current level start, error check
  668.             // internal stack.
  669.             //
  670.             state->level++;
  671.             if (state->level > (sizeof(stack) / sizeof(stack[0])))
  672.                 error("Nesting too deep: %d:", state->level);
  673.             stack[state->level] = point - 1;
  674.         } else if (closing_end(c)) {
  675.             //
  676.             // decrement the stack level and use the previous
  677.             // enclosing level as the current.  Validate that the
  678.             // level end characters match the opening delimiter.
  679.             //
  680.             if (state->level >= 0
  681.                 && matching_end(character(stack[state->level])) == c)
  682.                 state->level--;
  683.             else if (state->level >= 0) {
  684.                 point = stack[state->level];
  685.                 error("Looking for %c", character(stack[state->level]));
  686.             }
  687.         } else if (c == '"' || c == '\'') {
  688.             char strpat[8];
  689.             //
  690.             // within a string or an escaped character, move along
  691.             //
  692.             state->quoted = -1;
  693.             strcpy(strpat, "[X\n]");
  694.             strpat[1] = c;
  695.             while (re_search(1, strpat)
  696.                    && is_slashified(1)
  697.                    && point < to);
  698.             if (point < to)
  699.                 state->quoted = 0;
  700.         } else if (c == '*' || c == '/') {
  701.             //
  702.             // C/C++ comment, pass over the comment and reset state
  703.             // if the end of the comment is not past our end limit.
  704.             //
  705.             state->incomment = 1;
  706.             state->beginning_of_comment = point-2;
  707.             state->quoted = -2;
  708.             if (c == '*') {
  709.                 SEARCH_FORWARD("*/");
  710.             } else {
  711.                 nl_forward();
  712.             }
  713.             if (point <= to) {
  714.                 state->incomment = 0;
  715.                 state->beginning_of_comment = 0;
  716.                 state->quoted = 0;
  717.             }
  718.         }
  719.     }
  720.     if (state->level >= 0)
  721.         state->containing_cexp = stack[state->level];
  722. }
  723.  
  724.  
  725. ////////////////////////////////////////////////////////////////////////////
  726. //
  727. // c_indenter_1 will indent the current line given an assumption of C
  728. // syntax.
  729. //
  730.  
  731. c_indenter_1(state)
  732.     PSTATE *state;
  733. {
  734.     int indent;
  735.     int pos;
  736.  
  737.     //
  738.     // validate that we have a bit of whitespace at the end of the
  739.     // buffer to avoid boundary issues in the rest of the system.
  740.     //
  741.     if (point == size()) {
  742.         insert(' ');
  743.         point--;
  744.     }
  745.  
  746.     //
  747.     // obtain the first best guess for the indent level before we
  748.     // special case the current line.
  749.     //
  750.     pos = size() - point;
  751.     indent = calculate_c_indent(state);
  752.     to_begin_line();
  753.     if (indent == -1)
  754.         //
  755.         // This is inside a string constant
  756.         //
  757.         indent = current_indentation();
  758.     else if (indent == -2)
  759.         //
  760.         // This is currently in a comment
  761.         //
  762.         indent = calculate_c_indent_in_comment();
  763.     else if (LOOKING_AT("[ \t]*#"))
  764.         //
  765.         // We are indenting a preprocessor directive which is always
  766.         // at the left edge.
  767.         //
  768.         indent = 0;
  769.     else {
  770.         //
  771.         // Standard code state, special case particular syntactic
  772.         // structures.
  773.         //
  774.         SKIP_CHARS_FORWARD(" \t");
  775.         if (LOOKING_AT("case[ \t'(]|default:")) {
  776.             //
  777.             // we are in a switch statement and indenting a case
  778.             // keyword.
  779.             //
  780.             if (state->containing_cexp >= 0) {
  781.                 int save_excursion = point;
  782.  
  783.                 point = state->containing_cexp;
  784.                 indent = current_indentation() +
  785.                     c_case_offset + c_indent_level;
  786.                 point = save_excursion;
  787.             }
  788.             if (indent < 0)
  789.                 indent = 0;
  790.         } else if (LOOKING_AT("[A-Za-z0-9$_]+:")) {
  791.             //
  792.             // This is a label or C++ class keyword
  793.             //
  794.             indent += c_label_offset;
  795.             if (indent < 0)
  796.                 indent = 0;
  797.         } else if (LOOKING_AT("else[ \t\n]")) {
  798.             int save_excursion = point;
  799.             //
  800.             // This is an else, match to the opening if
  801.             //
  802.             c_backward_to_start_of_if(state->beginning_of_defun);
  803.             indent = current_indentation();
  804.             point = save_excursion;
  805.         } else if (LOOKING_AT("}[ \t]*else")) {
  806.             int save_excursion = point;
  807.             //
  808.             // This is a cuddly else, we have special knowledge that
  809.             // this else lines up with the if with the opening brace.
  810.             //
  811.             point++;
  812.             BACKWARD_SEXP();     // to begin of open brace
  813.             BACKWARD_SEXP();     // to begin of open paren
  814.             indent = current_indentation();
  815.             point = save_excursion;
  816.         } else if (LOOKING_AT("while[ \t\n]")) {
  817.             int save_excursion = point;
  818.             //
  819.             // This is a while, check if it is part of a do-while
  820.             // loop.  If it is not then break to the default else for
  821.             // standard indent.
  822.             //
  823.             if (!c_backward_to_start_of_do(state->beginning_of_defun)) {
  824.                 point = save_excursion;
  825.                 goto next;        // just use the default else
  826.             }
  827.             //
  828.             // This is a `while' that ends a do-while.
  829.             //
  830.             indent = current_indentation();
  831.             point = save_excursion;
  832.         } else
  833.       next:
  834.         //
  835.         // This is a standard C statement, do nothing special unless
  836.         // this is an open or close brace.
  837.         //
  838.         if (character(point) == '}')
  839.             indent -= (c_indent_level - c_brace_offset);
  840.         else if (character(point) == '{')
  841.             indent += c_brace_offset;
  842.     }
  843.     to_indentation();
  844.     //
  845.     // Check for the special case of the first or last opening brace
  846.     // in the function.
  847.     //
  848.     if (character(point) == '{'
  849.         && c_brace_offset
  850.         && state->containing_cexp <= 0)
  851.         indent -= c_brace_offset;
  852.     else if (character(point) == '}') {
  853.         int save_excursion = point;
  854.  
  855.         point = state->containing_cexp;
  856.         if (BOLP())
  857.             indent = 0;
  858.         point = save_excursion;
  859.     }
  860.     //
  861.     // do the indent only if we are not already there
  862.     //
  863.     if (current_column() != indent)
  864.         INDENT_TO(indent);
  865.     //
  866.     // If initial point was within line's indentation, position after
  867.     // the indentation.  Else stay at same point in text.
  868.     //
  869.     if (size() - pos > point)
  870.         point = size() - pos;
  871.     return (indent);
  872. }
  873.  
  874.  
  875. ////////////////////////////////////////////////////////////////////////////
  876. //
  877. // c_indenter sets up the protect unwind code, inits the state
  878. // variable and calls the primary indenter.
  879. //
  880.  
  881. c_indenter()
  882. {
  883.     PSTATE state;
  884.     jmp_buf this_level;
  885.     jmp_buf *old_level = top_level;
  886.     int ret;
  887.  
  888.     save_var case_fold;
  889.  
  890.     case_fold = 0;
  891.     top_level = &this_level;
  892.     if (ret = setjmp(top_level)) {
  893.         restore_vars();
  894.         top_level = old_level;
  895.         longjmp(top_level, ret);
  896.     }
  897.     state.beginning_of_defun = -1;
  898.     ret = c_indenter_1(&state);
  899.     top_level = old_level;
  900.     return ret;
  901. }
  902.  
  903.  
  904. ////////////////////////////////////////////////////////////////////////////
  905. //
  906. // c_backward_to_exp_start will move to the first character of the
  907. // line containing the beginning of the current sub expression.
  908. //
  909.  
  910. c_backward_to_exp_start(lim)
  911.     int lim;
  912. {
  913.     if (index(")\"", character(point - 1)))
  914.         BACKWARD_SEXP();
  915.     to_begin_line();
  916.     if (point <= lim)
  917.         point = lim + 1;
  918.     SKIP_CHARS_FORWARD(" \t");
  919. }
  920.  
  921.  
  922. ////////////////////////////////////////////////////////////////////////////
  923. //
  924. // c_backward_to_start_of_do will if point follows a `do' statement,
  925. // move to beginning of it and return TRUE.  Otherwise return FALSE
  926. // and don't move point.
  927. //
  928.  
  929. c_backward_to_start_of_do(limit)
  930.     int limit;
  931. {
  932.     int save_excursion;
  933.     int first = 1;
  934.     int startpos = point;
  935.     int done = 0;
  936.  
  937.     if (limit < 0)
  938.         limit = 0;
  939.  
  940.     //
  941.     // loop until we find an open do or move out of the current
  942.     // enclosing c expression.
  943.     //
  944.     while (!done) {
  945.         c_backward_to_noncomment(limit);
  946.         if (!BACKWARD_SEXP())
  947.             //
  948.             // We are at the top of the buffer, return the start
  949.             // position and bug out.
  950.             //
  951.             done = 2;
  952.         else if (LOOKING_AT("do[^A-Za-z0-9_$]"))
  953.             //
  954.             // We found an open do
  955.             //
  956.             done = 1;
  957.         else {
  958.             //
  959.             // Otherwise, if we skipped a semicolon, we lose.
  960.             // (Exception: we can skip one semicolon before getting
  961.             // to the last token of the statement, unless that token
  962.             // is a close brace).
  963.             //
  964.             save_excursion = point;
  965.             if (FORWARD_SEXP()) {
  966.                 if (!first && character(point - 1) == '}')
  967.                     done = 2;
  968.                 if (!done && character(point) == ';' &&
  969.                     character(point - 1) == '}')
  970.                     done = 2;
  971.                 if (!done && character(point) == ';') {
  972.                     if (first) {
  973.                         if (character(point - 1) == ')' && BACKWARD_SEXP()) {
  974.                             if (BACKWARD_SEXP() &&
  975.                                 LOOKING_AT("while") &&
  976.                                 c_backward_to_start_of_do(limit))
  977.                                 continue;
  978.                         }
  979.                     }
  980.                     if (!first)
  981.                         done = 2;
  982.                     first = 0;
  983.                 }
  984.                 point = save_excursion;
  985.             }
  986.         }
  987.         //
  988.         // If we go too far back in the buffer, we lose.
  989.         //
  990.         if (point < limit && !done)
  991.             done = 2;
  992.     }
  993.     if (done != 1)
  994.         point = startpos;
  995.     return (done == 1);
  996. }
  997.  
  998.  
  999. ////////////////////////////////////////////////////////////////////////////
  1000. //
  1001. // c_backward_to_start_of_if will move to the start of the last
  1002. // "unbalanced" `if'.
  1003. //
  1004.  
  1005. c_backward_to_start_of_if(limit)
  1006.     int limit;
  1007. {
  1008.     int if_level = 1;
  1009.  
  1010.     if (limit < 0)
  1011.         limit = 0;
  1012.     while (point > 0 && if_level > 0) {
  1013.         if (!BACKWARD_SEXP()) {
  1014.             say("Syntax error");
  1015.             break;
  1016.         }
  1017.         if (LOOKING_AT("else[^A-Za-z0-9$_]"))
  1018.             if_level++;
  1019.         else if (LOOKING_AT("if[^A-Za-z0-9$_]"))
  1020.             if_level--;
  1021.         else if (point < limit) {
  1022.             if_level = 0;
  1023.             point = limit;
  1024.         }
  1025.     }
  1026. }
  1027.  
  1028.  
  1029. ////////////////////////////////////////////////////////////////////////////
  1030. //
  1031. // c_backward_to_noncomment will move in the reverse direction over
  1032. // all whitespace until it runs into some *real* code.
  1033. //
  1034.  
  1035. c_backward_to_noncomment(lim)
  1036.     int lim;
  1037. {
  1038.     int opoint = 0;
  1039.     int stop = 0;
  1040.  
  1041.     if (lim < 0)
  1042.         lim = 0;
  1043.     while (!stop) {
  1044.         //
  1045.         // move over the trailing whitespace
  1046.         //
  1047.         SKIP_CHARS_BACKWARD(" \t\n\f");
  1048.         opoint = point;
  1049.         //
  1050.         // move through standard comments
  1051.         //
  1052.         if (point >= 2 + lim) {
  1053.             point -= 2;
  1054.             if (LOOKING_AT("%*/")) {
  1055.                 if (!SEARCH_BACKWARD("/*"))
  1056.                     point = lim > 0 ? lim : 0;
  1057.                 continue;
  1058.             }
  1059.             point += 2;
  1060.         }
  1061.         if (point <= lim)
  1062.             stop = 1;
  1063.         else {
  1064.             //
  1065.             // Check for C++ style comment
  1066.             //
  1067.             to_begin_line();
  1068.             SKIP_CHARS_FORWARD(" \t");
  1069.             if ((character(point) != '#') && !LOOKING_AT("//")) {
  1070.                 PSTATE ps;
  1071.                 int from = point;
  1072.                 int to;
  1073.  
  1074.                 to_end_line();
  1075.                 to = point;
  1076.                 point = from;
  1077.                 parse_partial_cexp(from, to, &ps);
  1078.                 stop = 1;
  1079.                 if (ps.incomment) {
  1080.                     point = ps.beginning_of_comment;
  1081.                     SKIP_CHARS_BACKWARD(" \t");
  1082.                     continue;
  1083.                 }
  1084.             }
  1085.             point = opoint;
  1086.             if (!stop) {
  1087.                 to_begin_line();
  1088.             }
  1089.         }
  1090.     }
  1091. }
  1092.  
  1093.  
  1094. ////////////////////////////////////////////////////////////////////////////
  1095. //
  1096. // calculate_c_indent_in_comment will return the appropriate indent
  1097. // for this comment line.  This is special cased for block comments.
  1098. //
  1099.  
  1100. calculate_c_indent_in_comment()
  1101. {
  1102.     int indent = 0;
  1103.  
  1104.     save_var point;
  1105.  
  1106.     to_begin_line();
  1107.     if (point > 0) {
  1108.         SKIP_CHARS_BACKWARD(" \t\n");
  1109.         to_begin_line();
  1110.         SKIP_CHARS_FORWARD(" \t");
  1111.         indent = current_column();
  1112.         if (LOOKING_AT("/%*"))
  1113.             indent++;
  1114.     }
  1115.     return (indent);
  1116. }
  1117.  
  1118.  
  1119. ////////////////////////////////////////////////////////////////////////////
  1120. //
  1121. // calculate_c_indent will return the appropriate indentation for the
  1122. // current line as C code.  In usual case an integer is returned: the
  1123. // column to indent to.  Returns -1 if line starts inside a string,
  1124. // -2 if in a comment.
  1125. //
  1126.  
  1127. calculate_c_indent(state)
  1128.     PSTATE *state;
  1129. {
  1130.     int parse_start = 0;
  1131.     int indent_point;
  1132.     int ret = 0;
  1133.     int containing_cexp = -1;
  1134.     PSTATE ps;
  1135.  
  1136.     save_var point;
  1137.  
  1138.     //
  1139.     // init
  1140.     //
  1141.     if (!state) {
  1142.         state = &ps;
  1143.         state->beginning_of_defun = -1;
  1144.     }
  1145.     state->containing_cexp = containing_cexp;
  1146.     to_begin_line();
  1147.     //
  1148.     // Check for previous line comment
  1149.     //
  1150.     if (c_line_comment_align) {
  1151.         int save_excursion_1 = point;
  1152.         if (nl_reverse()) {
  1153.             to_begin_line();
  1154.             if (LOOKING_AT("[ \t]*//")) {
  1155.                 to_indentation();
  1156.                 return current_column();
  1157.             }
  1158.         }
  1159.         point = save_excursion_1;
  1160.     }
  1161.     if (point > 0) {
  1162.         //
  1163.         // parse to the beginning of this function
  1164.         //
  1165.         indent_point = point;
  1166.         if (state->beginning_of_defun == -1) {
  1167.             BEGINNING_OF_DEFUN();
  1168.             state->beginning_of_defun = point;
  1169.         }
  1170.         //
  1171.         // determine the current level and save the point of the
  1172.         // current enclosing C expression
  1173.         //
  1174.         point = state->beginning_of_defun;
  1175.         ret = 0;
  1176.         parse_start = point;
  1177.         parse_partial_cexp(point, indent_point, state);
  1178.         containing_cexp = state->containing_cexp;
  1179.         if (state->instring || state->incomment) {
  1180.             //
  1181.             // return -1 or -2 if this line is part of a standard C style
  1182.             // comment block.
  1183.             //
  1184.             ret = state->quoted;
  1185.         } else if (containing_cexp < 0) {
  1186.             //
  1187.             // Line is at top level.  May be data or function
  1188.             // definition, or may be function argument declaration.
  1189.             // Indent like the previous top level line unless that
  1190.             // ends in a closeparen without semicolon, in which case
  1191.             // this line is the first argument decl.
  1192.             //
  1193.             point = indent_point;
  1194.             SKIP_CHARS_FORWARD(" \t");
  1195.             if (character(point) == '{')
  1196.                 ret = 0;        /* Unless it starts a function body */
  1197.             else if (c_argdecl_indent > 0) {
  1198.                 //
  1199.                 // Check for non-ansi argument declarations
  1200.                 //
  1201.                 c_backward_to_noncomment(parse_start > 0 ? parse_start : 0);
  1202.                 //
  1203.                 // look at previous line that's at column 0 to
  1204.                 // determine whether we are in top-level decls or
  1205.                 // function's arg decls.
  1206.                 //
  1207.                 re_search(-1, "^[^ \f\t\n#]");
  1208.                 if (LOOKING_AT("[A-Za-z0-9$_]+[^\"\n=]*%(")) {
  1209.                     point = matchend - 1;
  1210.                     FORWARD_SEXP();
  1211.                     SKIP_CHARS_FORWARD(" \t\f");
  1212.                     if (point < indent_point
  1213.                         && !index(",;", character(point)))
  1214.                         ret = c_argdecl_indent;
  1215.                 }
  1216.             }
  1217.         } else if (character(containing_cexp) != '{') {
  1218.             //
  1219.             // line is expression, not statement: indent to just
  1220.             // after the surrounding open.
  1221.             //
  1222.             point = containing_cexp + 1;
  1223.             ret = current_column();
  1224.         } else {
  1225.             //
  1226.             // Statement level.  Is it a continuation or a new
  1227.             // statement Find previous non-comment character.
  1228.             //
  1229.             point = indent_point;
  1230.             c_backward_to_noncomment(containing_cexp);
  1231.             //
  1232.             // Back up over previous parameter continuations since
  1233.             // they don't affect the continuation indent.
  1234.             //
  1235.             while (character(point-1) == ',' && (point > containing_cexp)) {
  1236.                 BACKWARD_SEXP();
  1237.                 to_begin_line();
  1238.                 c_backward_to_noncomment(containing_cexp);
  1239.             }
  1240.             //
  1241.             // Back up over label lines since they don't affect
  1242.             // whether our line is a continuation.  Do not back up
  1243.             // over case lines or C++ class keywords.
  1244.             //
  1245.             {
  1246.                 int stop = 0;
  1247.                 int save_excursion_1 = point;
  1248.  
  1249.                 while (character(point-1) == ':' &&
  1250.                        !stop && (point > containing_cexp)) {
  1251.                     to_indentation();
  1252.                     if (LOOKING_AT("case"))
  1253.                         stop = 1;
  1254.                     else {
  1255.                         c_backward_to_noncomment(containing_cexp);
  1256.                         save_excursion_1 = point;
  1257.                     }
  1258.                 }
  1259.                 point = save_excursion_1;
  1260.             }
  1261.             //
  1262.             // Check for a preprocessor statement or its continuation
  1263.             // lines. Move back to end of previous non-preprocessor
  1264.             // line.
  1265.             //
  1266.             {
  1267.                 int found = point;
  1268.                 int stop = 0;
  1269.                 int save_excursion_1;
  1270.  
  1271.                 while (!stop) {
  1272.                     save_excursion_1 = point;
  1273.                     if (point > 0)
  1274.                         to_end_line();
  1275.                     if ((character(point - 1)) != '\\') {
  1276.                         point = save_excursion_1;
  1277.                         //
  1278.                         // This line is not preceded by a backslash.
  1279.                         // So either it starts a preprocessor command
  1280.                         // or any following continuation lines should
  1281.                         // not be skipped.
  1282.                         //
  1283.                         to_indentation();
  1284.                         if (character(point) == '#') {
  1285.                             to_end_line();
  1286.                             found = point;
  1287.                         } else
  1288.                             stop = 1;
  1289.                     } else
  1290.                         nl_reverse();
  1291.                 }
  1292.                 point = found;
  1293.                 //
  1294.                 // Now we get the answer.
  1295.                 //
  1296.                 save_excursion_1 = point;
  1297.                 point = indent_point;
  1298.                 SKIP_CHARS_FORWARD(" \t");
  1299.                 //
  1300.                 // Don't treat a line with a close-brace as a
  1301.                 // continuation.  It is probably the end of an enum,
  1302.                 // struct, union or other type declaration.
  1303.                 //
  1304.                 if ((character(point)) != '}') {
  1305.                     point = save_excursion_1;
  1306.                     if ((point > 0) && !index(",;{}", character(point - 1))) {
  1307.                         //
  1308.                         // This line is continuation of preceding
  1309.                         // line's statement indent
  1310.                         // c_continued_statement_offset more than the
  1311.                         // previous line of the statement.
  1312.                         //
  1313.                         c_backward_to_exp_start(containing_cexp);
  1314.                         ret = current_column();
  1315.                         point = indent_point;
  1316.                         SKIP_CHARS_FORWARD(" \t");
  1317.                         if (character(point) != '{')
  1318.                             ret += c_continued_statement_offset;
  1319.                     } else
  1320.                         goto new_statement;
  1321.                 } else
  1322.             new_statement:
  1323.                 {
  1324.                     //
  1325.                     // This line starts a new statement. Position
  1326.                     // following last unclosed open.
  1327.                     //
  1328.                     point = containing_cexp;
  1329.                     //
  1330.                     // Is line first statement after an open-brace or
  1331.                     // after a case. If no, find that first statement
  1332.                     // and indent like it.
  1333.                     //
  1334.                     point++;
  1335.                     {
  1336.                         int colon_line_end = 0;
  1337.  
  1338.                         while (SKIP_CHARS_FORWARD(" \t\n"),
  1339.                                LOOKING_AT("#|/%*|case[ \t\n'(].*:|[a-zA-Z0-9_$]*:")) {
  1340.                             //
  1341.                             // Skip over comments and labels
  1342.                             // following openbrace.
  1343.                             //
  1344.                             if (character(point) == '#')
  1345.                                 nl_forward();
  1346.                             else if (character(point) == '/') {
  1347.                                 point += 2;
  1348.                                 SEARCH_FORWARD("*/");
  1349.                             } else {
  1350.                                 //
  1351.                                 // case, C++ class keyword or label:
  1352.                                 //
  1353.                                 save_excursion_1 = point;
  1354.                                 to_end_line();
  1355.                                 colon_line_end = point;
  1356.                                 point = save_excursion_1;
  1357.                                 SEARCH_FORWARD(":");
  1358.                             }
  1359.                         }
  1360.                         //
  1361.                         // The first following code counts if it is
  1362.                         // before the line we want to indent.
  1363.                         //
  1364.                         if (point < indent_point
  1365.                             && colon_line_end > 0
  1366.                             && colon_line_end < indent_point) {
  1367.                             if (colon_line_end > point)
  1368.                                 ret = current_indentation() - c_label_offset;
  1369.                             else if (colon_line_end > 0
  1370.                                      && colon_line_end > containing_cexp) {
  1371.                                 save_excursion_1 = point;
  1372.                                 point = colon_line_end;
  1373.                                 to_indentation();
  1374.                                 //
  1375.                                 // chack for switch syntax element
  1376.                                 //
  1377.                                 if (LOOKING_AT("case[ \t'(]|default:")) {
  1378.                                     ret = current_column();
  1379.                                     point = indent_point;
  1380.                                     SKIP_CHARS_FORWARD(" \t");
  1381.                                     if (!LOOKING_AT("[{}]"))
  1382.                                         ret += c_indent_level;
  1383.                                     else
  1384.                                         ret -= (c_case_offset +
  1385.                                                 c_brace_offset);
  1386.                                     point = save_excursion_1;
  1387.                                 } else {
  1388.                                     point = save_excursion_1;
  1389.                                     ret = current_column();
  1390.                                 }
  1391.                             } else
  1392.                                 goto no_previous;
  1393.                         } else
  1394.                     no_previous:
  1395.                         {
  1396.                             //
  1397.                             // If no previous statement, indent it
  1398.                             // relative to line brace is on (or the
  1399.                             // last case statement).  For open brace
  1400.                             // in column zero, don't let statement
  1401.                             // start there too.  If c_indent_level is
  1402.                             // zero, use c_brace_offset +
  1403.                             // c_continued_statement_offset instead.
  1404.                             // For open-braces not the first thing in
  1405.                             // a line, add in
  1406.                             // c_brace_imaginary_offset.
  1407.                             //
  1408.                             point = containing_cexp;
  1409.                             if (BOLP() && !c_indent_level)
  1410.                                 ret = c_brace_offset + 
  1411.                                     c_continued_statement_offset;
  1412.                             else if (BOLP())
  1413.                                 ret = c_indent_level;
  1414.                             else
  1415.                                 ret = c_indent_level - c_brace_offset;
  1416.                             //
  1417.                             // Move back over whitespace before the
  1418.                             // openbrace. If openbrace is not first
  1419.                             // nonwhite thing on the line, add the
  1420.                             // c_brace_imaginary_offset.
  1421.                             //
  1422.                             SKIP_CHARS_BACKWARD(" \t");
  1423.                             if (!BOLP())
  1424.                                 ret += c_brace_imaginary_offset;
  1425.                             //
  1426.                             // If the openbrace is preceded by a
  1427.                             // parenthesized exp, move to the
  1428.                             // beginning of that; possibly a
  1429.                             // different line.
  1430.                             //
  1431.                             if (character(point - 1) == ')')
  1432.                                 BACKWARD_SEXP();
  1433.                             //
  1434.                             // Get initial indentation of current line.
  1435.                             //
  1436.                             ret += current_indentation();
  1437.                         }
  1438.                     }
  1439.                 }
  1440.             }
  1441.         }
  1442.     }
  1443.     return (ret);
  1444. }
  1445.  
  1446.  
  1447. ////////////////////////////////////////////////////////////////////////////
  1448. //
  1449. // Epsilon Commands
  1450. //
  1451. //
  1452.  
  1453. keytable c_tab;                    /* key table for c mode */
  1454.  
  1455. char c_mode_name[] = "NewC";
  1456.  
  1457.  
  1458. ////////////////////////////////////////////////////////////////////////////
  1459. //
  1460. // c_mode sets up the mode and correctly sets comment hooks
  1461. //
  1462.  
  1463. command
  1464. c_mode()
  1465. {
  1466.     mode_keys = c_tab;            /* use these keys */
  1467.     c_tab[')'] = c_tab[']'] = Matchdelim ? (short) show_matching_delimiter : 0;
  1468.     indenter = c_indenter;
  1469.     auto_indent = 1;
  1470.     major_mode = c_mode_name;
  1471.     strcpy(comment_start, "(/</|*>)[ \t]*");
  1472.     strcpy(comment_pattern, "//.*$|/<*>(.|<newline>)*<*>/<FirstEnd>");
  1473.     if (new_c_comments) {
  1474.         strcpy(comment_begin, "// ");
  1475.         strcpy(comment_end, "");
  1476.     } else {
  1477.         strcpy(comment_begin, "/* ");
  1478.         strcpy(comment_end, " */");
  1479.     }
  1480.     try_calling("c-mode-hook");
  1481.     make_mode();
  1482. }
  1483.  
  1484.  
  1485. ////////////////////////////////////////////////////////////////////////////
  1486. //
  1487. // do_c_indent is a standard tab indent.  Repeated tabs will add tabs
  1488. // into the current location rather than repeating c_indent on every tab.
  1489. //
  1490.  
  1491. do_c_indent()
  1492.     on  c_tab[CTRL('I')]
  1493. {
  1494.     int save_excursion = point;
  1495.  
  1496.     if (!c_tab_always_indent) {
  1497.         if (prev_cmd == C_INDENT)    /* repeated, make bigger */
  1498.             to_column(current_column() + tab_size -
  1499.                       current_column() % tab_size);
  1500.         else {
  1501.             /* If not in indentation, do a tab */
  1502.             SKIP_CHARS_BACKWARD(" \t");
  1503.             if (!BOLP()) {
  1504.                 point = save_excursion;
  1505.                 to_column(current_column() + tab_size -
  1506.                           current_column() % tab_size);
  1507.                 return;
  1508.             }
  1509.             point = save_excursion;
  1510.             c_indenter();
  1511.         }
  1512.     } else
  1513.         c_indenter();
  1514.     this_cmd = C_INDENT;
  1515. }
  1516.  
  1517.  
  1518. ////////////////////////////////////////////////////////////////////////////
  1519. //
  1520. // do_c_newline is the enter hook.  This routine will reindent if the
  1521. // current line is one of several syntax block begins.
  1522. //
  1523.  
  1524. do_c_newline()
  1525.     on  c_tab[CTRL('M')], c_tab[CTRL('J')]
  1526. {
  1527.     spot save_excursion = alloc_spot();
  1528.  
  1529.     if (auto_indent) {
  1530.         to_indentation();
  1531.         if (LOOKING_AT("#|((case|else)[ \t]*)|[A-Za-z0-9$_]+:|((while|if|switch|for)[ \t]*%(.*%))"))
  1532.             c_indenter();
  1533.         point = *save_excursion;
  1534.         free_spot(save_excursion);
  1535.         insert('\n');
  1536.         c_indenter();
  1537.     } else
  1538.         insert('\n');
  1539. }
  1540.  
  1541.  
  1542. ////////////////////////////////////////////////////////////////////////////
  1543. //
  1544. // c_open and c_close are the open and close block hooks
  1545. //
  1546.  
  1547. c_open()
  1548.     on  c_tab['{']
  1549. {
  1550.     normal_character();
  1551.     if (auto_indent)
  1552.         c_indenter();
  1553. }
  1554.  
  1555.  
  1556. c_close()
  1557.     on  c_tab['}']
  1558. {
  1559.     normal_character();
  1560.     if (auto_indent)
  1561.         c_indenter();
  1562.     if (Matchdelim)
  1563.         find_delimiter();
  1564. }
  1565.  
  1566.  
  1567. ////////////////////////////////////////////////////////////////////////////
  1568. //
  1569. // forward_cexp and backward_cexp will move to the next (or previous)
  1570. // c_exp in the current function.
  1571. //
  1572.  
  1573. command
  1574. forward_cexp()
  1575.     on  c_tab[ALT(CTRL('F'))]
  1576. {
  1577.     int start = point;
  1578.     int last = size();
  1579.  
  1580.     if (!traverse_cexp(iter < 0 ? -1 : 1)) {
  1581.         point = start;
  1582.         quick_abort();
  1583.     }
  1584.     while (index(",:;\\", character(point)) && (point < last))
  1585.         point++;
  1586. }
  1587.  
  1588.  
  1589. command
  1590. backward_cexp()
  1591.     on  c_tab[ALT(CTRL('B'))]
  1592. {
  1593.     int start = point;
  1594.  
  1595.     while ((point > 0) && index(",:;\\ \t\n\f", character(point - 1)))
  1596.         point--;
  1597.     if (!traverse_cexp(iter < 0 ? 1 : -1)) {
  1598.         point = start;
  1599.         quick_abort();
  1600.     }
  1601. }
  1602.  
  1603.  
  1604. ////////////////////////////////////////////////////////////////////////////
  1605. //
  1606. // kill_cexp will remove the cexp at the current cursor
  1607. //
  1608.  
  1609. command
  1610. kill_cexp()
  1611.     on  c_tab[ALT(CTRL('K'))]
  1612. {
  1613.     int start = point;
  1614.     int end;
  1615.  
  1616.     if (!traverse_cexp(iter < 0 ? -1 : 1)) {
  1617.         point = start;
  1618.         quick_abort();
  1619.     }
  1620.     end = point;
  1621.     if (end < start) {
  1622.         point = end;
  1623.         end = start;
  1624.         start = point;
  1625.     } else
  1626.         point = start;
  1627.     iter = 0;
  1628.     do_save_kill(start, end);
  1629. }
  1630.  
  1631.  
  1632. ////////////////////////////////////////////////////////////////////////////
  1633. //
  1634. // start_of_defun and end_of_defun will move to the beginning (or
  1635. // end) of the current function.
  1636. //
  1637.  
  1638. command
  1639. start_of_defun()
  1640.     on  c_tab[ALT(CTRL('A'))]
  1641. {
  1642.     iter = 0;
  1643.     beginning_of_defun();
  1644.     window_start = prev_screen_line(3);
  1645. }
  1646.  
  1647.  
  1648. command
  1649. end_of_defun()
  1650.     on  c_tab[ALT(CTRL('E'))]
  1651. {
  1652.     int orig = point;
  1653.  
  1654.     iter = 0;
  1655.     FORWARD_SEXP();
  1656.     point++;
  1657.     if (BEGINNING_OF_DEFUN()) {
  1658.         while (FORWARD_SEXP() && (character(point - 1) != '}'));
  1659.         if (character(point - 1) != '}')
  1660.             point = orig;
  1661.         else if (character(point) == ';')
  1662.             point++;
  1663.     }
  1664. }
  1665.  
  1666.  
  1667. ////////////////////////////////////////////////////////////////////////////
  1668. //
  1669. // up_level will move up one enclosing C block in the current function
  1670. //
  1671.  
  1672. command
  1673. up_level()
  1674.     on  c_tab[ALT(CTRL('U'))]
  1675. {
  1676.     PSTATE state;
  1677.     int orig = point;
  1678.  
  1679.     iter = 0;
  1680.     BEGINNING_OF_DEFUN();
  1681.     while (point <= orig) {
  1682.         parse_partial_cexp(point, orig + 1, &state);
  1683.     }
  1684.     if (state.containing_cexp >= 0)
  1685.         point = state.containing_cexp;
  1686.     else {
  1687.         point = orig;
  1688.         error("No containing cexp");
  1689.     }
  1690. }
  1691.  
  1692.  
  1693. ////////////////////////////////////////////////////////////////////////////
  1694. //
  1695. // indent_function will reformat the current function by indenting
  1696. // every line from the beginning of the function.
  1697. //
  1698.  
  1699. command
  1700. indent_function()
  1701.     on  c_tab[ALT(CTRL('Q'))]
  1702. {
  1703.     PSTATE ps;
  1704.     jmp_buf this_level;
  1705.     jmp_buf *old_level = top_level;
  1706.     int ret;
  1707.     spot stop = 0;
  1708.  
  1709.     save_spot point;
  1710.     save_var case_fold;
  1711.  
  1712.     iter = 0;
  1713.     top_level = &this_level;
  1714.     case_fold = 0;
  1715.     if (ret = setjmp(top_level)) {
  1716.         if (stop)
  1717.             free_spot(stop);
  1718.         restore_vars();
  1719.         top_level = old_level;
  1720.         longjmp(top_level, ret);
  1721.     }
  1722.     beginning_of_defun();
  1723.     ps.beginning_of_defun = point;
  1724.     end_of_defun();
  1725.     stop = alloc_spot();
  1726.     point = ps.beginning_of_defun;
  1727.     while (point < *stop) {
  1728.         c_indenter_1(&ps);
  1729.         nl_forward();
  1730.         check_abort();
  1731.     }
  1732.     free_spot(stop);
  1733.     top_level = old_level;
  1734.     this_cmd = 0;
  1735. }
  1736.  
  1737.  
  1738. ////////////////////////////////////////////////////////////////////////////
  1739. //
  1740. // mark_function will highlight the current function
  1741. //
  1742.  
  1743. command
  1744. mark_function()
  1745. {
  1746.     iter = 0;
  1747.     beginning_of_defun();
  1748.     set_mark();
  1749.     end_of_defun();
  1750.     exchange_point_and_mark();
  1751. }
  1752.  
  1753.  
  1754. ////////////////////////////////////////////////////////////////////////////
  1755. //
  1756. // list_functions will show all functions within the current file.
  1757. //
  1758.  
  1759. #define FUNCTION_BUFFER "*Functions*"
  1760.  
  1761. command
  1762. list_functions()
  1763.     on  c_tab[ALT('#')]
  1764. {
  1765.     char buff[512];
  1766.     int brace;
  1767.     int thisbuf = bufnum;
  1768.     int funcbuf;
  1769.     int oldpoint = point;
  1770.  
  1771.     iter = 0;
  1772.     point = 0;
  1773.     funcbuf = zap(FUNCTION_BUFFER);
  1774.     while (SEARCH_FORWARD("\n{")) {
  1775.         brace = point;
  1776.         point++;
  1777.         beginning_of_defun();
  1778.         if (SEARCH_FORWARD("(")) {
  1779.             if (point < brace) {
  1780.                 point--;
  1781.                 re_search(-1, "[a-zA-Z0-9_]+");
  1782.                 grab(matchstart, matchend, buff);
  1783.                 bufnum = funcbuf;
  1784.                 bprintf("%s\n", buff);
  1785.                 bufnum = thisbuf;
  1786.             }
  1787.         }
  1788.         point = brace;
  1789.         end_of_defun();
  1790.     }
  1791.     point = oldpoint;
  1792.     sort_another(FUNCTION_BUFFER, 0, 0);
  1793.     view_buffer(FUNCTION_BUFFER, 1);
  1794. }
  1795.